package naito_rescue.communication.manager;

import java.util.*;
import sample.*;
import rescuecore2.messages.*;
import rescuecore2.worldmodel.*;
import rescuecore2.Constants;
import rescuecore2.config.*;
import rescuecore2.standard.components.*;
import rescuecore2.standard.entities.*;
import rescuecore2.standard.messages.*;
import rescuecore2.misc.geometry.*;

import naito_rescue.*;
import naito_rescue.agent.*;
import naito_rescue.communication.team.*;
import naito_rescue.communication.team.generator.*;
import naito_rescue.communication.channel.*;
import naito_rescue.object.*;
import naito_rescue.message.*;
import naito_rescue.message.manager.*;


public class CommunicationTeamManager
{
	private List<RadioChannel> radioChannels;
	private List<VoiceChannel> voiceChannels;
	private NAITOAgent owner;
	private StandardWorldModel model;
	private MyLogger           logger;
	private Config config;
	private List<FireBrigade> allFB;
	private List<PoliceForce> allPF;
	private List<AmbulanceTeam> allAT;
	private List<StandardEntity> allPlatoons;
	private TeamsProvider        provider;
	
	public CommunicationTeamManager(NAITOAgent owner, Config config){
		this.owner = owner;
		this.model = owner.getWorldModel();
		this.config = config;
		this.logger = owner.getLogger();
		this.radioChannels = new ArrayList<RadioChannel>();
		this.voiceChannels = new ArrayList<VoiceChannel>();
		this.provider = new TeamsProvider();
		
		//logger.info("CommunicationTeamManager#<Constructor>()");
		initPlatoons();
		initChannels();
		
		//Providerを色々登録
		//radioChannelsの数によって，渡すplatoonのサイズを変える
		//->残りはNoCommuで対応
		//--> VoiceChannelへは常にメッセージをリレーする
		ICommunicationTeamsGenerator basic = 
			new BasicCommunicationTeamGenerator(this.allPlatoons, this.radioChannels, this.config, this.logger);
		this.provider.registerBasicTeamGenerator(basic);
		ICommunicationTeamsGenerator nocomm = 
			new NoCommunicationTeamGenerator(this.allPlatoons, voiceChannels.get(0), this.config, this.logger);
		this.provider.registerTeamGenerator("NOCOMM", nocomm);
		//logger.info("CommunicationTeamManager#<Constructor>() end;");
	}
	private void initPlatoons(){
		this.allFB = new ArrayList<FireBrigade>();
		this.allPF = new ArrayList<PoliceForce>();
		this.allAT = new ArrayList<AmbulanceTeam>();
		this.allPlatoons = new ArrayList<StandardEntity>();
		this.allFB.addAll(this.owner.getFBList());
		this.allPF.addAll(this.owner.getPFList());
		this.allAT.addAll(this.owner.getATList());
		this.allPlatoons.addAll(this.allFB);
		this.allPlatoons.addAll(this.allPF);
		this.allPlatoons.addAll(this.allAT);
		
		//logger.info("CommunicationTeamManager#initPlatoons();");
		//logger.debug("allFB.size() = " + this.allFB.size());
		//logger.debug("allPF.size() = " + this.allPF.size());
		//logger.debug("allAT.size() = " + this.allAT.size());
		//logger.info("CommunicationTeamManager#initPlatoons() end;");
	}
	//RadioChannel, VoiceChannelを初期化	
	// failure, dropoutは先延ばし -> 別メソッドにした方が吉
	private void initChannels(){
		AbstractChannel channel = null;
		//logger.info("CommunicationTeamManager#initChannels()");
		
		int numOfChannel = config.getIntValue("comms.channels.count", -1);
		//logger.debug("comms.channels.count = " + numOfChannel);
		if(numOfChannel != -1){
			for(int i = 0;i < numOfChannel;i++){
				String  channelInfo = "comms.channels." + i + "";
				String  type = config.getValue(channelInfo + ".type");
				int bandwidth = 0, messageCount = 0, range = 0;
				boolean useInputFailure = false, useOutputDropout = false;
				double inputFailureP = 0.0, outputDropoutP = 0.0;
				if(type.equals("radio")){
					bandwidth = config.getIntValue(channelInfo + ".bandwidth");
					/*
					useInputFailure = config.getBooleanValue(channelInfo + ".noise.input.failure.use");
					inputFailureP = 0.0;
					if(useInputFailure){
						inputFailureP = config.getFloatValue(channelInfo + ".noise.input.failure.p");
					}
					useOutputDropout = config.getBooleanValue(channelInfo + ".noise.output.dropout.use");
					outputDropoutP = 0.0;
					if(useOutputDropout){
						outputDropoutP = config.getFloatValue(channelInfo + ".noise.output.dropout.p");
					}
					*/
					channel = new RadioChannel(i, bandwidth, inputFailureP, outputDropoutP);
					radioChannels.add((RadioChannel)channel);
				}else if(type.equals("voice")){
					bandwidth = config.getIntValue(channelInfo + ".messages.size");
					messageCount = config.getIntValue(channelInfo + ".messages.max");
					range = config.getIntValue(channelInfo + ".range");
					/*
					useInputFailure = config.getBooleanValue(channelInfo + ".noise.input.failure.use");
					inputFailureP = 0.0;
					if(useInputFailure){
						inputFailureP = config.getFloatValue(channelInfo + ".noise.input.failure.p");
					}
					
					useOutputDropout = config.getBooleanValue(channelInfo + ".noise.output.dropout.use");
					outputDropoutP = 0.0;
					if(useOutputDropout){
						outputDropoutP = config.getFloatValue(channelInfo + ".noise.output.dropout.p");
					}
					*/
					channel = new VoiceChannel(i, bandwidth, messageCount, range, inputFailureP, outputDropoutP);
					voiceChannels.add((VoiceChannel)channel);
				}
				if(channel != null){
					//logger.debug("One " + (type.equals("radio")?"Radio":"Voice") + " channel found.");
					//logger.debug("bandwidth = " + channel.getBandwidth());
					//logger.debug("InputFailure prob = " + channel.getInputFailureProb());
					//logger.debug("OutputDropout prob = " + channel.getOutputDropoutProb());
				}
			}
		}
		//logger.info("CommunicationTeamManager#initChannels() end;");
	}
	// シナリオの状況に合わせてチーム群を生成
	// Providerから引っ張りだして生成させる
	public List<ICommunicationTeam> generateTeams(){
		List<ICommunicationTeam> teamList = null;
		
		//logger.info("CommunicationTeamManager#generateTeams();");
		// TODO NoCommuなどの対応
		if(radioChannels.size() >= 1){
			teamList = this.provider.generateBasicTeams();
		}else if(voiceChannels.size() >= 1){
			//logger.info("No-Communication teams are generated.", "CommunicationTeamManager#generateTeams();");
			teamList = this.provider.generateTeams("NOCOMM");
		}
		
		//logger.info("CommunicationTeamManager#generateTeams() end;");
		return teamList;
	}
	public VoiceChannel getVoiceChannel(){
		return voiceChannels.get(0);
	}
	// TeamsGeneratorの登録，チーム群の引っ張り出し
	private class TeamsProvider
	{
		public static final String BASIC_PROVIDER_KEY = "BASIC";
		private Map<String, ICommunicationTeamsGenerator> providers;
		
		public TeamsProvider(){
			this.providers = new HashMap<String, ICommunicationTeamsGenerator>();
		}
		public void registerBasicTeamGenerator(ICommunicationTeamsGenerator generator){
			//logger.info("TeamsProvider#registerBasicTeamGenerator()");
			registerTeamGenerator(BASIC_PROVIDER_KEY, generator);
			//logger.info("TeamsProvider#registerBasicTeamGenerator() end;");
		}
		public void registerTeamGenerator(String key, ICommunicationTeamsGenerator generator){
			//logger.info("TeamsProvider#registerTeamGenerator();");
			this.providers.put(key, generator);
			//logger.info("TeamsProvider#registerTeamGenerator() end;");
		}
		public List<ICommunicationTeam> generateTeams(String key){
			ICommunicationTeamsGenerator generator = providers.get(key);
			if(generator == null){
				//エラー処理
			}
			return generator.generateTeams();
		}
		public List<ICommunicationTeam> generateBasicTeams(){
			return generateTeams(BASIC_PROVIDER_KEY);
		}
	}
}
